package com.qilin.hammer;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.Namespace;
import org.dom4j.io.SAXReader;
import org.restlet.data.Form;
import org.restlet.resource.ClientResource;

import com.qilin.hammer.util.BaseRestService;

class Data{
	String name;
	String label;
	String path;
	String namespace;
	String type;
	
	Data(String n, String l, String p, String ns){
		name = n;
		type = "";
		label = l;
		path = p;
		namespace = ns;
	}
	
	Data(String p, String ns){
		name = "";
		label = "";
		path = p;
		namespace = ns;
	}
	
	public boolean equals(Data d){
		if(this == d)
			return true;
		else{
			if(d.path.equals(this.path)&&d.namespace.equals(this.namespace))
				return true;
		}
		return false;
	}
}

class Node{
	String xpath;
	String namespace;
	Element element;
	
	Node(String p, String np, Element e){
		this.xpath = p;
		this.namespace = np;
		this.element = e;
	}
}

public class FeedMetaTester extends BaseRestService{
	
	public FeedMetaTester(String name) {
		super(name);
	}
	private static Log logger;
	private static String FEED_PATH_PREFIX = "/prj/home/vtbaadmin/FeedMetaTest$Deployment/";
	
	private ArrayList<String> testFeeds = new ArrayList<String>();
	private static String folder = "D:/work/FeaturesTestOwned/FeedMetadata/injector/";
	
	{
		testFeeds.add("feeds/attributefeed");
		
		testFeeds.add("feeds/q_uq/extend");
		testFeeds.add("feeds/simplefeed");
		
		testFeeds.add("feeds/q_uq/q_uq_nest_ce_diff");
		testFeeds.add("feeds/q_uq/q_uq_nest_ce_same");
		testFeeds.add("feeds/q_uq/q_uq_nest_ce_same_noarray");
		testFeeds.add("feeds/q_uq/q_uq_nest_ct_diff");
		testFeeds.add("feeds/q_uq/q_uq_nest_ct_same");
		testFeeds.add("feeds/q_uq/q_uq_nest_ct_same_noarray");
		testFeeds.add("feeds/q_uq/q_uq_se");
		testFeeds.add("feeds/q_uq/q_uq_unest_ce");
		testFeeds.add("feeds/q_uq/q_uq_unest_ct");
		
		testFeeds.add("feeds/uq_uq/nest_ce_diff");
		testFeeds.add("feeds/uq_uq/nest_ce_same");
		testFeeds.add("feeds/uq_uq/nest_ce_same_noarray");
		testFeeds.add("feeds/uq_uq/nest_ct_diff");
		testFeeds.add("feeds/uq_uq/nest_ct_same");
		testFeeds.add("feeds/uq_uq/nest_ct_same_noarray");
		testFeeds.add("feeds/uq_uq/uq_uq_se");
		testFeeds.add("feeds/uq_uq/unest_ce");
		testFeeds.add("feeds/uq_uq/unest_ct");
		
		testFeeds.add("feeds/q_q/q_q_unest_ce");
		testFeeds.add("feeds/q_q/q_q_unest_ct");

	}
	

	static{
		logger=LogFactory.getLog(FeedMetaTester.class);
	}
	
	public FeedMetaTester() {
		super("");
	}
	
	private String getNamespaceIfExists(Element e){
		if(e.getNamespace().equals(Namespace.NO_NAMESPACE))
			return "unqualified---->";
		else 
			return e.getNamespace().getText()+"---->";
	}
	
	private void addAttributesUnderElement(Element e, String namespace, ArrayList<Data> eventData){
		List attributes = e.attributes();
		for(Object obj: attributes){
			Attribute attr = (Attribute)obj;
			String ns = attr.getNamespaceURI();
			String xpath = "";
			String fullpath = attr.getPath();
			if(fullpath.startsWith("/"))
				fullpath = fullpath.substring(1);
			String [] pathes = fullpath.split("/");
			for(String path:pathes){
				if(path.indexOf(":")!=-1){
					if(path.startsWith("@"))
						xpath+="@"+path.split(":")[1];
					else
						xpath+=path.split(":")[1];
				}else{
					xpath+=path;
				}
				xpath+="/";
			}
			xpath = xpath.substring(0,xpath.length()-1);
			if(ns.equals(""))
				eventData.add(new Data(xpath, namespace + "unqualified"));
			else
				eventData.add(new Data(xpath, namespace + ns));
		}
	}
	
	private void doWalk(Node currentNode, ArrayList<Data> eventData){
		Element element = currentNode.element;
		String xpath = currentNode.xpath + element.getName()+"/";
		String namespace = currentNode.namespace + getNamespaceIfExists(element);
		this.addAttributesUnderElement(element, namespace, eventData);
		if(element.elements().size()!=0){
			for(Object obj : element.elements())
				doWalk(new Node(xpath, namespace, (Element)obj), eventData);
		}else{
			xpath = xpath.substring(0, xpath.length()-1);
			namespace = namespace.substring(0, namespace.length()-5);
			eventData.add(new Data(xpath, namespace));
		}
	}
	
	//Get path from feed injector data
	private ArrayList<Data> getEventData(String file){
		Document eventDoc = this.getDocFromFile(file);
		ArrayList<Data> eventData = new ArrayList<Data>();
		if(eventDoc != null){
			Element root = eventDoc.getRootElement();
			String xpath = "", namespace = "";
			namespace += getNamespaceIfExists(root);
			xpath += root.getName()+"/";
			this.addAttributesUnderElement(root, namespace, eventData);
			if(root.elements().size()!=0){
				for(Object obj : root.elements())
					doWalk(new Node(xpath, namespace, (Element)obj), eventData);
			}else{
				logger.info("Only root document.");
			}
		}
		return eventData;
	}
	
	private Document getDocFromFile(String file){
		SAXReader reader = new SAXReader();
		File eventdoc = new File(file);
		Document document = null;
		if(eventdoc.exists()){
			try {
				document = reader.read(eventdoc);
			} catch (DocumentException e) {
				e.printStackTrace();
			}
		}
		return document;
	}
	

//	private ArrayList<Data> getMetaData(String feedpath, BaseRestService restService){
//		ArrayList<Data> results = type.equals(FeedMetaTester.REST)?this.getMetaDataFromRest(path):this.getMetaDataFromFile(path);

//		return results;
//	}
	
	//Get path from feed metadata
	private Data fetchData(String linetext){
		Matcher lineMatcher = null;
		Pattern pattern = Pattern.compile("(/(@?\\{[^\\{\\}]+\\}:)?[@\\w]+){1}");
		int name_begin = linetext.indexOf("name=")+6;
		int name_end = linetext.indexOf("\"",name_begin);
		if(name_begin==5)
			return null;
		String name = linetext.substring(name_begin, name_end);
		  
		int label_begin = linetext.indexOf("label=")+7;
		int label_end = linetext.indexOf("\"",label_begin);
		String label = linetext.substring(label_begin, label_end);
		  
		int path_begin = linetext.indexOf("path=")+6;
		int path_end = linetext.indexOf("\"",path_begin);
		String path = linetext.substring(path_begin, path_end);
		
		lineMatcher = pattern.matcher(path);
		String xpath = "";
		String namespace = "";
		while(lineMatcher.find()){
			String subpath = lineMatcher.group();
			int mid = subpath.lastIndexOf(":");
			if(mid != -1){
				if(subpath.charAt(1)=='@'){
					xpath += "@"+subpath.substring(mid+1, subpath.length())+"/";
					namespace += subpath.substring(3,mid-1)+"---->";
				}else{
					xpath +=  subpath.substring(mid+1, subpath.length())+"/";
					namespace += subpath.substring(2,mid-1)+"---->";
				}
			}else{
				xpath += subpath.substring(1)+"/";
				namespace+="unqualified---->";
			}
		}
		return new Data(name, label, xpath.substring(0, xpath.length()-1), namespace.substring(0, namespace.length()-5));
	}
	
	private ArrayList<Data> getMetaDataFromRest(String feedname, BaseRestService restService){
		String feedpath = FEED_PATH_PREFIX + feedname;
		String restUrl = BaseRestService.URL_PREFIX + feedpath;
		ClientResource client = restService.createSecureClientResource(restUrl);
		ArrayList<Data> results = new ArrayList<Data>();
		try {
			String text = client.get().getText();
			Document doc = DocumentHelper.parseText(text);
			List contents = doc.getRootElement().element("contents").elements("content");
			String metadata = "";
			for(Object obj:contents){
				String name = ((Element)obj).attributeValue("name");
				if(name.equals("feed.metadata"))
					metadata = ((Element)obj).getStringValue();
			}
			logger.debug(metadata);
			Matcher matcher  = Pattern.compile("<result\\b.+>").matcher(metadata);
			while(matcher.find()){
				Data data = fetchData(matcher.group());
				if(data != null)
					results.add(data);
			}
		} catch (Exception e) {
			  e.printStackTrace();
		}
		if(logger.isDebugEnabled())
			this.showResults(results);
		return results;
	}
	
	private ArrayList<Data> getMetaDataFromFile(String filepath){
		ArrayList<Data> results = new ArrayList<Data>();
		try {
			BufferedReader br = new BufferedReader(new FileReader(filepath));
			String linetext = null;
			while((linetext=br.readLine())!=null){
				Data data =  fetchData(linetext);
				if(data != null)
					results.add(data);
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		if(logger.isDebugEnabled())
			this.showResults(results);
		return results;
	}
	
	private void showResults(ArrayList<Data> results){
		logger.debug("Results:");
		for(Data data:results){
			logger.debug("xpath: "+data.path);
			logger.debug("namespace: "+data.namespace);
			logger.debug("");
		}
	}
	
	//Run tests
	private boolean doCompare(ArrayList<Data> metaData, ArrayList<Data> eventData){
		boolean isMatch = true;
		if(metaData.size()!=eventData.size()){
			isMatch = false;
			for(int i=0; i<metaData.size(); i++){
				logger.info("Feed MetaData: ");
				logger.info("label: "+metaData.get(i).label);
				logger.info("xpath: "+metaData.get(i).path);
				logger.info("namespace: "+metaData.get(i).namespace);
				logger.info("");
			}
			for(int i=0; i<eventData.size(); i++){
				logger.info("Event data: ");
				logger.info("xpath: "+eventData.get(i).path);
				logger.info("namespace: "+eventData.get(i).namespace);
				logger.info("");
			}
		}else{
			for(int i=0; i<metaData.size(); i++){
				if(!metaData.get(i).equals(eventData.get(i))){
					isMatch = false;
					logger.info("Feed MetaData: ");
					logger.info("label: "+metaData.get(i).label);
					logger.info("xpath: "+metaData.get(i).path);
					logger.info("namespace: "+metaData.get(i).namespace);
					logger.info("Event data: ");
					logger.info("xpath: "+eventData.get(i).path);
					logger.info("namespace: "+eventData.get(i).namespace);
					logger.info("");
				}
			}
		}
		if(isMatch)
			logger.info("Result: Match!!!");
		else
			logger.info("Result: Not match!!!");
		return isMatch;
		
	}
	
	public boolean compare(String feedpath, BaseRestService restService, String eventdatafile){
		ArrayList<Data> metaData = this.getMetaDataFromRest(feedpath, restService);
		ArrayList<Data> eventData = getEventData(eventdatafile);
		logger.info("Feed metadata is " + feedpath + " <---> Feed injector file is: "+eventdatafile.replace(folder, ""));
		return this.doCompare(metaData, eventData);
	}
	
	public boolean compare(String filepath, String eventdatafile){
		ArrayList<Data> metaData = this.getMetaDataFromFile(filepath);
		ArrayList<Data> eventData = getEventData(eventdatafile);
		return this.doCompare(metaData, eventData);
	}
	
	public void testAll(BaseRestService restService){
		int success = 0;
		int failure = 0;
		for(String feedpath:this.testFeeds){
			boolean result = compare(feedpath,restService,folder+feedpath.substring(feedpath.lastIndexOf("/")+1)+".xml");
			if(result == true)
				success++;
			else
				failure++;
		}
		logger.info("Success total: "+ success);
		logger.info("Failure total: "+ failure);
	}
	/**
	 * @param args
	 */
	public static void main(String[] args) {
		//new FeedMetaTester().getMetaData("D:/qulifiedcomplextype.txt", FeedMetaTester.FILE);
		//new FeedMetaTester().getEventData("d:/eventdoc.xml");
		//new FeedMetaTester().compare("D:/qulifiedcomplextype.txt", FeedMetaTester.FILE, "d:/eventdoc.xml");
		//BaseRestService restService = new BaseRestService();
		//restService.login();
		//new FeedMetaTester().compare("feeds/simplefeed", restService, "d:/eventdoc.xml");
		//new FeedMetaTester().testAll(restService);
		//restService.logout();
	}

}
